package semantic;

import java.util.ArrayList;

import syntax.CGParserDumpVisitor;

public class SymbolTable {
	private int Depth;
	private ArrayList<List> scopeDisplay;
	private HashTable hashTable;
	
	public SymbolTable(){
		Depth = 0;
		hashTable = new HashTable();
		scopeDisplay = new ArrayList<List>();
		scopeDisplay.add(new List());
	}
	
	public void Error(String errorinfo,String name){
		System.out.println(errorinfo+name);
	}
	
	public void OpenScope(){
		Depth += 1;
		if (scopeDisplay.size() <= Depth)
			scopeDisplay.add(new List());
		else
			scopeDisplay.set(Depth, new List());
	}
	
	public void CloseScope(){
		for(Symbol sym = scopeDisplay.get(Depth).getHead();sym != null;sym = sym.getNext()){
			Symbol prevsym = sym.getVar();
			hashTable.delete(sym);
			if (prevsym != null){
				hashTable.add(prevsym);
			}
		}
		Depth -= 1;
	}
	public int getOffsetAfterClosing(){
		Symbol sym = scopeDisplay.get(Depth).getHead();
		if(sym==null){
			return CGParserDumpVisitor.getOffset();
		}
		return sym.getAttr().offset;
	}
	public void deleteSymbol(Symbol sym){
		scopeDisplay.get(Depth).remove(sym);
		Symbol prevsym = sym.getVar();
		hashTable.delete(sym);		
		if (prevsym != null)
			hashTable.add(prevsym);	
	}
	public Symbol RetrieveSymbol(String name){
		List start = hashTable.get(name);
		if (start == null)
			return null;
		Symbol sym = start.getHead();
		while (sym != null){
			if (sym.getName().equals(name))
				return sym;
			sym = sym.getNext();
		}
		return null;
	}
	
	public void EnterSymbol(String name, Attribute attr){
		Symbol oldsym = RetrieveSymbol(name);
		if (oldsym != null && oldsym.getDepth() == Depth){
			Error("Duplicate definition of ",name);
			return;
		}
		Symbol newsym = new Symbol(name,attr);
		newsym.setLevel(scopeDisplay.get(Depth));
		newsym.setDepth(Depth);
		scopeDisplay.get(Depth).insert(newsym);
		
		if (oldsym == null)
			hashTable.add(newsym);
		else{
			hashTable.delete(oldsym);
			hashTable.add(newsym);
		}
		newsym.setVar(oldsym);
	}
	
	public boolean DeclaredLocally(String name){
		for(Symbol start = scopeDisplay.get(Depth).getHead();start !=null;start = start.getNext())
			if (start.getName().equals(name))
				return true;
		return false;
	}
}
class HashTable{
	private List[] table;
	
	public HashTable(){
		table = new List[50];
	}
	
	public int ELFHash(String str){
		long hash = 0;  
		long x = 0;
		for(int i = 0; i < str.length(); i++){  
			hash = (hash << 4) + str.charAt(i);            
			if((x = hash & 0xF0000000L) != 0)
				hash ^= (x >> 24);   
			hash &= ~x;
		}
		return (int) (hash % 50);
	}
	
	void add(Symbol sym){
		int index = ELFHash(sym.getName());
		if (table[index] == null) {
			table[index] = new List();
			table[index].insert(sym);
		}
		else {
			table[index].insert(sym);
		}
	}
	void delete(Symbol sym){
		int index = ELFHash(sym.getName());
		if (table[index] != null)
			table[index].remove(sym);
	}
	List get(String name){
		int index = ELFHash(name);
		return table[index];
	}
}

class List{
	private Symbol head;
	private Symbol rear;
	
	public List(){
		head = null;
		rear = null;
	}
	
	public void insert(Symbol sym){
		Symbol s = new Symbol (sym.getName(), sym.getAttr());
		if (head == null){
			head = rear = s;
		}
		else{
			Symbol temp = head;
			while (temp.getNext() != null) temp = temp.getNext();
			temp.setNext(s);
		}
	}
	public void remove(Symbol sym){
		Symbol start = head;
		if (start == null)return;
		else{
			if (start.getName().equals(sym.getName())){
				if (start.getNext() == null){
					head = null;
					return;
				}
				else {
					head = start.getNext();
					return;
				}
			}
		}
		for (start = head;start.getNext() != null && start.getNext().getName() != sym.getName();start =  start.getNext());
		if (start.getNext() ==null) return;
		start.setNext(start.getNext().getNext());
	}
	public Symbol getHead(){
		return head;
	}
}